/**
 * Copyright (C) 2010 Cloudfarming <info@cloudfarming.nl>
 *
 * Licensed under the Eclipse Public License - v 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.eclipse.org/legal/epl-v10.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package nl.cloudfarming.client.messaging.inbox;

import java.beans.PropertyChangeListener;
import java.util.List;
import javax.persistence.Query;
import nl.cloudfarming.client.messaging.Message;
import nl.cloudfarming.client.messaging.MessageEventKey;
import nl.cloudfarming.client.messaging.MessageModule;
import nl.cloudfarming.client.model.CommonModelService;
import nl.cloudfarming.client.model.DataProvider;
import nl.cloudfarming.eventbus.GuiEvent;
import nl.cloudfarming.eventbus.GuiEventKey;
import nl.cloudfarming.eventbus.GuiEventListener;
import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Collections;
import javax.swing.ActionMap;
import org.openide.explorer.ExplorerManager;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.Lookup;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.lookup.InstanceContent;

/**
 * Service for messaging module
 * 
 * @author Gerben Feenstra & Merijn Zengers
 */
@ServiceProviders(value = {
    @ServiceProvider(service = DataProvider.class),
    @ServiceProvider(service = MessageInbox.class),
    @ServiceProvider(service = GuiEventListener.class)
})
public class MessageInbox extends CommonModelService implements GuiEventListener<Message> {

    private MessageNotifier messageNotifier;
    private final List<Message> messages;
    public static final String PROPERTY_ADDED = "added";
    private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
    private final InstanceContent content = new InstanceContent();
    private final Lookup lookup = new AbstractLookup(content);

    public MessageInbox() {
        messageNotifier = new MessageNotifier();
        messages = Collections.synchronizedList(findAllMessages());
    }

    /**
     * Returns the module name
     * @return Name of this module
     */
    @Override
    public String getModuleName() {
        return MessageModule.MODULE_NAME;
    }

    /**
     * returns a list with al the available messages
     * @return List of Message
     */
    public List<Message> getMessages() {
        return messages;
    }

    /**
     * Adds a message to the inbox and persists it.
     * @param message the message to be add
     */
    void addMessageToInbox(Message message) {
        if (!message.isTransientMessage()){
            persistInTransaction(message);
        }
        messages.add(message);
        propertyChangeSupport.firePropertyChange(PROPERTY_ADDED, null, message);
    }

    /**
     * Find and return all messages in the database
     * @return List of messages
     */
    private List<Message> findAllMessages() {
        startTransaction();
        Query query = getEntityManager().createNamedQuery(Message.Q_FIND_ALL_MESSAGES);
        return query.getResultList();
    }

    /**
     * Update existing message. For example set a message as "read"
     * @param toBeMerged The message instance to merge with the existing persisted message
     */
    void updateMessage(Message toBeMerged) {
        startTransaction();
        if (toBeMerged.getId() == 0) {
            throw new IllegalArgumentException("The message cannot be updated because it is not persisted yet.");
        } else {
            toBeMerged = getEntityManager().merge(toBeMerged);
        }
        commit();
    }

    /**
     * Not used for this module
     */
    @Override
    protected void initExplorer() {
        Node rootNode = new AbstractNode(Children.create(new MessageNodeFactory(), true));
        ExplorerManager manager = new ExplorerManager();
        manager.setRootContext(rootNode);
        manager.getRootContext().setDisplayName("messages");

        setExplorerManager(manager, new ActionMap());
    }

    @Override
    public void onEvent(GuiEvent<Message> event) {
        if (MessageEventKey.NEW_MESSAGE.equals(event.getKey())) {
            //First persist the message
            addMessageToInbox(event.getContent());

            //Notify everybody 
            messageNotifier.showNotifier(event);

        } else if (MessageEventKey.MESSAGE_READ.equals(event.getKey())) {
            messageNotifier.removeNotifier(event);
        }
    }

    @Override
    public boolean listensTo(GuiEventKey guiEventKey) {
        return MessageEventKey.NEW_MESSAGE.equals(guiEventKey) || MessageEventKey.MESSAGE_READ.equals(guiEventKey);
    }

    /**
     * Adds property change listener which listens to messages added to this inbox
     * @param pcl the property change listener
     */
    public void addMessagePropertyChangeListener(PropertyChangeListener pcl) {
        propertyChangeSupport.addPropertyChangeListener(PROPERTY_ADDED, pcl);
    }

    public void removeMessagePropertyChangeListener(PropertyChangeListener pcl) {
        propertyChangeSupport.removePropertyChangeListener(PROPERTY_ADDED, pcl);
    }

    @Override
    public Lookup getLookup() {
        return this.lookup;
    }
}
